#ifndef __tenacitas_command__receiver__h__
#define __tenacitas_command__receiver__h__


// =============================================================================
/** @file
    This file defines the @p receiver class
*/

// =============================================================================
//                                 C++ Headers
#include <cstdint>
#include <thread>

// =============================================================================
//                                 3rds Headers

// =============================================================================
//                                 Our Headers
#include "processor.h"

// =============================================================================
//                                 Namespaces
using namespace std;

// =============================================================================
//                                 Macro Contants

// =============================================================================
//                                 Macro Commands

/** namespace of the organization */
namespace tenacitas {
    /** A library which provides classes that help writing software that have to
        receive and handle commands

        This library is based on the books: "Internetworking with TCP/IP",
        volume 3, by Douglas E. Comer and David L. Stevens, published by
        Prentice Hall; and "Unix Network Programming", by Richard Stevens,
        published by Prentice Hall */   
    namespace command {

        // =========================== Typedefs ===========================

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================

        /** Receives commands, possibly composed by many messages, through some
            type of connection, translates the messages in some type of specific
            data, and passes them to some type of application, using some type of
            concurrence stratergy  

            
            ###################################################################
            @tparam t_message_factory defines the type of message, and the type
            of message pointer.

            It must implement:

            <code>
            message, the type of the message that compounds a command

            message_ptr, the type of pointer to @p message 
            <endcode>

            ###################################################################
            @p t_connector_factory creates objects that puts the receiver ready
            to accept connections, and actually accepts connections

            must implement:

            <code>
            connector, which puts the receiver ready to accept connections, and
            actually accepts connections 

            connector_ptr, type of pointer to the @p connector

            connection, through where messages will arrive, and will through
            where they will be send

            connection_ptr, type of pointer to the @p connection
            
            static connector_ptr create( ), which created a @p connector pointer
            <endcode>

            ###################################################################
            @p connector puts the receiver ready to accept connections, and
            actually accepts connections.

            It must implement:
            <code>

            bool start( ), which makes the connector ready to accept connections
            
            connection_ptr connected( ), which creates a connection to the
            (client) software that will send and receive messages

            <endcode>

            ###################################################################
            @tparam t_concurrence_factory crates objects that are responsible for
            controlling the concurrence of message handling.
            
            It must implement:
            <code>
            concurrence, which is the type of concurrence used

            concurrence_ptr, type of pointer to the @p concurrence

            static concurrence_ptr create( ), which creates a @p concurrence
            pointer 
            <endcode>

            ###################################################################
            @p concurrence must implement:

            <code>
            void operator( )( tenacitas_command::
            processor<t_message_factory,
            t_connector_factory,
            t_message_io_factory,
            t_translator_factory,
            t_application_factory> & p_processor),
            which will start processing messages by calling
            <tt>processor( )</tt> 
            <endcode>

            ###################################################################
            @p t_message_io_factory is responsible for creating objects that will
            read messages from a @p t_connector_factory::connection, and write
            messages through a @p t_connector_factory::connection  

            It must implement:
            <code>
            message_io

            message_io_ptr

            static message_io_ptr create( )
            <endcode>

            ###################################################################
            @p message_io is responsible for reading messages from a @p
            connection, and writing messages through a @p
            t_connector_factory::connection. 

            It must implement:
            <code>
            t_message_factory::message_ptr
            read( t_connector_factory::connection_ptr p_con ), which reads a
            message from a @p t_connector_factory::connection  

            void write( t_connector_factory::connection_ptr p_con,
            t_message_factory::message_ptr p_message ), which writes
            a message through a @p t_connector_factory::connection   


            ###################################################################
            @tparam t_translator_factory creates objects that will translate a @p
            t_message_factory::message into an app_data to suitable for the @p
            t_application_factory::application

            It must implement:
            <code>
            translator

            translator_ptr

            app_data
            
            app_data_ptr

            static translator_ptr create( )
            <endcode>
            
            ###################################################################
            @p translator translate a @p t_message_factory::message object into
            into an app_data to suitable for the @p
            t_application_factory::application 

            It must implement:
            <code>

            t_translator_factory::app_data_ptr to_app_data
            ( const t_message_factory::message_ptr p_message );

            t_message_factory::message_ptr p_message to_message
            ( const t_translator_factory::app_data_ptr );

            <endcode>
            
            
            ###################################################################
            @tparam t_application_factory creates objects that handle the
            message read by a @p connection object.

            It must implement:
            <code>
            application

            application_ptr

            static application_ptr create( )
            <endcode>

            ###################################################################
            @p application is responsible for handling translated messages.

            It must implement:

            <code>

            void operator( )( t_translator_factory::app_data_ptr p_app_data,
            t_connector_factory::connection_ptr p_con,
            t_message_io_factory::message_io_ptr p_message_io );

            <endcode>

            @todo: define timeout for waiting output connection
            @todo: where to define timeout for reading message: in @p
            t_connector_factory::connection, or in the @p receiver?

	    ###################################################################
            @tparam t_timeout_factory creates @ptimeout objects that determine
            how long a @p t_application_factory::application must wait for a new
            @p t_message_factory::message. 

	    It must implement:
	    <code>
	    timeout, which defines for how long a @p
	    t_application_factory::application must wait for a new @p
	    t_message_factory::message. 

	    timeout_ptr, a reference to a @p timeout object

	    timeout_ptr static create( ), which creates references to @p timeout

	    ###################################################################
	    @p timeout defines for how long a @p
	    t_application_factory::application must wait for a new @p
	    t_message_factory::message. 

	    It must implement:

	    @todo define @p timeout interface
            
            @nosubgrouping 
        */

        template <
            typename t_message_types,
            typename t_connection_types,
            typename t_app_data_types,
            typename t_translator_types,
            typename t_concurrence_types,
            typename t_application_types,
            typename t_connector_types,
            typename t_timeout_types,
            typename t_message_io_types,
            typename t_log_types
            >
        class receiver {

        public:

            // --- receiver - Public Internal Types ------------------------

            typedef t_message_types message_types;
            typedef typename message_types::message message;
            typedef typename message_types::message_ptr message_ptr;

            typedef t_app_data_types app_data_types;
            typedef typename app_data_types::app_data app_data;
            typedef typename app_data_types::app_data_ptr app_data_ptr;

            typedef t_connection_types connection_types;
            typedef typename connection_types::connection connection;
            typedef typename connection_types::connection_ptr connection_ptr;

            typedef t_translator_types translator_types;
            typedef typename translator_types::translator_factory translator_factory;
            typedef typename translator_types::translator_factory_ptr
            translator_factory_ptr; 
            typedef typename translator_types::translator translator;
            typedef typename translator_types::translator_ptr translator_ptr;

            typedef t_concurrence_types concurrence_types;
            typedef typename concurrence_types::concurrence_factory concurrence_factory;
            typedef typename concurrence_types::concurrence_factory_ptr
            concurrence_factory_ptr; 
            typedef typename concurrence_types::concurrence concurrence;
            typedef typename concurrence_types::concurrence_ptr concurrence_ptr;

            typedef t_application_types application_types;
            typedef typename application_types::application_factory application_factory;
            typedef typename application_types::application_factory_ptr
            application_factory_ptr; 
            typedef typename application_types::application application;
            typedef typename application_types::application_ptr application_ptr;

            typedef t_connector_types connector_types;
            typedef typename connector_types::connector_factory connector_factory;
            typedef typename connector_types::connector_factory_ptr
            connector_factory_ptr; 
            typedef typename connector_types::connector connector;
            typedef typename connector_types::connector_ptr connector_ptr;

            typedef t_timeout_types timeout_types;
            typedef typename timeout_types::timeout_factory timeout_factory;
            typedef typename timeout_types::timeout_factory_ptr
            timeout_factory_ptr; 
            typedef typename timeout_types::timeout timeout;
            typedef typename timeout_types::timeout_ptr timeout_ptr;

            typedef t_message_io_types message_io_types;
            typedef typename message_io_types::message_io_factory message_io_factory;
            typedef typename message_io_types::message_io_factory_ptr
            message_io_factory_ptr; 
            typedef typename message_io_types::message_io message_io;
            typedef typename message_io_types::message_io_ptr message_io_ptr;


            typedef command::processor<message_types,
                                       connection_types,
                                       app_data_types,
                                       translator_types,
                                       application_types,
                                       timeout_types,
                                       message_io_types> processor;

            // --- receiver - Public Constructors --------------------------

            /** Constructor

                A @p receiver is created unable to receive messages.
                One must call @p start_sync or @p start_async so that @p receiver
                starts receiving messages 
            */
            receiver( translator_factory_ptr p_translator_factory,
                      concurrence_factory_ptr p_concurrence_factory,
                      application_factory_ptr p_application_factory, 
                      connector_factory_ptr p_connector_factory,
                      timeout_factory_ptr p_timeout_factory,
                      message_io_factory_ptr p_message_io_factory )
                : m_stop( true ),
                  m_start_th( ),
                  m_translator_factory( p_translator_factory ),
                  m_concurrence_factory( p_concurrence_factory ),
                  m_application_factory( p_application_factory ),
                  m_connector_factory( p_connector_factory ),
                  m_timeout_factory( p_timeout_factory ),
                  m_message_io_factory( p_message_io_factory ) {}

            /** Copy constructor not allowed */
            receiver( const receiver & ) = delete;
                

            /** Destructor */
            ~receiver( ) {
                if ( !m_stop ) {
                    m_stop = true;
                    if ( m_start_th != std::thread::thread( ) ) {
                        m_start_th.join( );
                    }
                }
            }
            

            // --- receiver - Public Processors ----------------------------

            /** Starts receiving messages asynchronously

                @param [in] p_connector determines the connection where the @p
                receiver will receive messages, and the connection where messages
                will be sent back to
            */
            void start_async( ) {
                if ( !m_stop ) {
                    return;
                    // @todo: log warn that the receiver was already started
                }
                m_stop = false;
                
                m_start_th = std::thread( receiver::start, *this );
            }

            /** Starts receiving messages synchronously

                @param [in] p_connector determines the connection where the @p
                receiver will receive messages, and the connection where messages
                will be sent back to
            */
            void start_sync( ) {
                if ( !m_stop ) {
                    return;
                    // @todo: log warn that the receiver was already started
                }

                m_stop = false;
                
                start( );
            }

            /** Stops receiving messages */
            void stop( ) {
                m_stop = true;
            }

            // --- receiver - Public Operators -----------------------------

            /** Assignment not allowed */
            receiver & operator=( const receiver & ) = delete;
            
        private:

            // --- receiver - Private Processors ---------------------------

            /** Starts receiving message data */
            void start( ) {

                // concurrence policy for processing messages
                concurrence_ptr l_concurrence = m_concurrence_factory->create( ); 

                // where will the messages be delivered?
                connector_ptr l_connector = m_connector_factory->create( );

                if ( !l_connector ) {
                    // @todo log critical; throw exception
                    std::cout << "Error creating connector" << std::endl;
                }

                // waits for connections
                if ( !l_connector->start( ) ) {
                    // @todo log critical; throw exception
                    std::cout << "Error starting connector" << std::endl;
                }
                

                // we will process messages, using the defined concurrence
                // policy, until someone tells us to stop
                while ( !m_stop ) {

                    std::cout << "Waiting for a connection...\n";

                    // wait until a connection to send messages to is defined,
                    // which is done by some, possibly, remote software
                    connection_ptr l_connection = l_connector->connected( );

                    if ( !l_connection ) {
                        // @todo log critical; throw exception
                        std::cout << "Error connection established, but empty" << std::endl;
                    }
                    else {
                        std::cout << "Connection established"  << std::endl;
                    }

                    // our message processor, a class to allow concurrent message
                    // processing invocation
                    processor l_processor( l_connection,
                                           m_translator_factory,
                                           m_application_factory,
                                           m_timeout_factory,
                                           m_message_io_factory );

                    // starting the message processing, possibly asynchronously
                    l_concurrence->submit( l_processor );
                }
            }

            // --- receiver - Private Operators ----------------------------

            // --- receiver - Private Attributes ---------------------------

            /** Flags that the @p m_start_th thread must stop */
            bool m_stop;
            

            /** Thread where the @p receiver will start receiving messages */
            std::thread m_start_th;

            translator_factory_ptr m_translator_factory;
            concurrence_factory_ptr m_concurrence_factory;
            application_factory_ptr m_application_factory;
            connector_factory_ptr m_connector_factory;
            timeout_factory_ptr m_timeout_factory;
            message_io_factory_ptr m_message_io_factory;
            

        };
    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::command;


#endif
